# coding=utf-8
import json

from django.conf import settings
from rest_framework.authentication import SessionAuthentication
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework import status
from rest_framework_xml.parsers import XMLParser
from wechatpy.pay import WeChatPayException

from sutras import enums
from sutras.models import (
    Temple as ModelTemple,
    PrintOrder as ModelPrintOrder,
    PrintOrderDetail as ModelPrintOrderDetail,
    UserTranscription as ModelUserTranscription,
    UserAddress as ModelUserAddress,
    UserLevelInfo as ModelUserLevelInfo,
)
from fe_api.print import serializers
from fe_api.print.utils import view_funcs
from sutras.utils.tools import generate_order_serial_num, init_wechat_pay


# 微信回调的Content-Type为 text/xml，而非 application/xml
XMLParser.media_type = "text/xml"


class SutraPrintConfig(APIView):

    authentication_classes = (SessionAuthentication,)
    permission_classes = (IsAuthenticated,)

    def get(self, request, user_transcription_id):
        """
        获取印刷配置
        """
        user_transcription = ModelUserTranscription.objects.filter(
            id=user_transcription_id,
            user=request.user).first()

        if not user_transcription:
            return Response({
                "status": 404,
                "msg": status.HTTP_404_NOT_FOUND,
            })

        if user_transcription.status != enums.UserTranscription.finished:
            return Response({
                "status": 403,
                "msg": status.HTTP_403_FORBIDDEN,
            })

        layout = user_transcription.user_layout
        font_color = layout.font_color.name
        material_name = layout.material.name
        book_binding = layout.bind.name
        sutra_edition = user_transcription.sutra_edition

        sutra_info = {
            "cover_url": "{0}{1}".format(
                settings.SELF_DOMAIN, sutra_edition.cover_url),
            "name": sutra_edition.name,
        }
        transcription_info = {
            "vow_time": user_transcription.vow_time,
            "finish_time": user_transcription.finish_time,
            "font_color": font_color,
            "material_name": material_name,
            "book_binding": book_binding,
        }

        context = {
            "status": 200,
            "msg": status.HTTP_200_OK,
            "data": {
                "sutra_info": sutra_info,
                "transcription_info": transcription_info,
            }
        }
        return Response(context)


class SutraPrintPriceCalculate(APIView):

    authentication_classes = (SessionAuthentication,)
    permission_classes = (IsAuthenticated,)

    def post(self, request, user_transcription_id):
        """
        计算总价格
        """
        user_transcription = ModelUserTranscription.objects.filter(
            id=user_transcription_id,
            user=request.user).first()

        if not user_transcription:
            return Response({
                "status": 404,
                "msg": "not found",
            })

        print_num = request.data.get("print_num")
        try:
            print_num = int(print_num)
        except ValueError:
            return Response({
                "status": 400,
                "msg": "parameter error",
            })

        price_info = view_funcs.calculate_order_price(
            user_transcription, print_num)

        return Response({
            "status": 200,
            "msg": "success",
            "data": price_info,
        })


class SutraPrintOrders(APIView):

    authentication_classes = (SessionAuthentication,)
    permission_classes = (IsAuthenticated,)

    def post(self, request, user_transcription_id):
        """
        提交印刷订单
        """
        print_data = {}
        delivery_list = request.data.get('delivery')
        donation_list = request.data.get('donation')

        user_transcription = ModelUserTranscription.objects.filter(
            id=user_transcription_id,
            user=request.user).first()

        if not user_transcription:
            return Response({
                "status": 404,
                "msg": status.HTTP_404_NOT_FOUND,
            })

        # 只能印刷抄写完的
        if user_transcription.status != enums.UserTranscription.finished:
            return Response({
                "status": 403,
                "msg": status.HTTP_403_FORBIDDEN,
            })

        # 计算总价格
        total_donation_num = sum(
            [int(donation["num"]) for donation in donation_list])
        total_delivery_num = sum(
            [int(delivery["num"]) for delivery in delivery_list])
        price_info = view_funcs.calculate_order_price(
            user_transcription,
            total_donation_num + total_delivery_num)

        serial_num = generate_order_serial_num()
        print_data["serial_num"] = serial_num
        print_data["user"] = request.user
        print_data["user_transcription"] = user_transcription
        print_data["sutra_edition"] = user_transcription.sutra_edition

        print_data["status"] = enums.PrintOrderStatus.unpaied
        print_data["pay_status"] = enums.PrintOrderStatus.unpaied
        print_data["total_price"] = price_info["total_price"]
        print_data["total_should_pay"] = price_info["total_should_pay"]
        print_order = ModelPrintOrder.objects.create(**print_data)

        # 创建订单详情
        to_create_order_details = []
        for delivery in delivery_list:
            address = ModelUserAddress.objects.get(
                id=delivery["address_id"], user=request.user)
            detail = {
                "type": enums.PrintDetailType.delivery,
                "print_order": print_order,
                "user": request.user,
                "print_num": delivery["num"],
                "contact": address.contact,
                "phone": address.phone,
                "address": address.address,
            }
            detail_obj = ModelPrintOrderDetail(**detail)
            to_create_order_details.append(detail_obj)
        for donation in donation_list:
            temple = ModelTemple.objects.get(id=donation["temple_id"])
            detail = {
                "type": enums.PrintDetailType.donation,
                "print_order": print_order,
                "user": request.user,
                "donate_num": donation["num"],
                "temple": temple,
            }
            detail_obj = ModelPrintOrderDetail(**detail)
            to_create_order_details.append(detail_obj)
        ModelPrintOrderDetail.objects.bulk_create(to_create_order_details)

        if price_info["total_price"]:
            # 创建微信支付订单
            pay_desc = settings.PRINT_WECHAT_ORDER_DESC.format(
                user_transcription.sutra_edition.name)
            wechat_pay = init_wechat_pay()
            try:
                res = wechat_pay.order.create(
                    "JSAPI",
                    pay_desc,
                    int(price_info["total_should_pay"] * 100),
                    settings.WECHAT_PRINT_PAY_NOTIFY_URL,
                    user_id=request.user.open_id,
                    out_trade_no=serial_num,
                    attach=json.dumps({"order_id": serial_num}))
            except WeChatPayException as e:
                # TODO: 此处处理异常情况
                raise e
            prepay_id = res["prepay_id"]  # 预支付ID
            wx_data = wechat_pay.jsapi.get_jsapi_params(prepay_id)
        else:
            # 免费的则直接完成
            view_funcs.finish_pay(serial_num)
            wx_data = None

        data = {
            "order_id": print_data["serial_num"],
            "total_price": price_info["total_price"],
            "total_should_pay": price_info["total_should_pay"],
            "wx_data": wx_data,
        }
        context = {
            "status": 200,
            "msg": status.HTTP_200_OK,
            "data": data}
        return Response(context)


class WechatNotify(APIView):

    authentication_classes = (SessionAuthentication,)
    permission_classes = (AllowAny,)
    parser_classes = (XMLParser,)

    def post(self, request):
        """
        微信支付回调
        """
        wechat_pay = init_wechat_pay()
        is_success = wechat_pay.check_signature(request.data)
        if not is_success:
            return Response({
                "status": 403,
                "msg": "forbidden",
            })

        order_id = request.data.get("out_trade_no")
        is_successful = view_funcs.finish_pay(order_id)
        if not is_successful:
            return Response({
                "status": 404,
                "msg": status.HTTP_404_NOT_FOUND,
            })

        return Response({
            "status": 200,
            "msg": status.HTTP_200_OK,
        })


class Temples(APIView):

    def get(self, request):
        """
        获取布施寺庙列表
        """
        limit = request.query_params.get('limit')
        offset = request.query_params.get('offset')
        temples = ModelTemple.objects.filter(
            status=enums.TempleStatus.normal)
        if limit and offset:
            temples = temples[int(offset):int(offset)+int(limit)]
        temples_data = serializers.TempleSerializer(temples, many=True).data
        context = {
            "status": 200,
            "msg": status.HTTP_200_OK,
            "data": temples_data}
        return Response(context)


class PrintInfo(APIView):

    authentication_classes = (SessionAuthentication,)
    permission_classes = (AllowAny,)

    def get(self, request, user_transcription_id):
        """
        获取抄写印刷信息
        """
        user_transcription = ModelUserTranscription.objects.filter(
            id=user_transcription_id).first()

        if not user_transcription:
            return Response({
                "status": 404,
                "msg": status.HTTP_404_NOT_FOUND,
            })

        print_orders = ModelPrintOrder.objects.filter(
            user=request.user,
            user_transcription__id=user_transcription_id,
            status=enums.PrintOrderStatus.paied)
        donate_details = ModelPrintOrderDetail.objects.filter(
            print_order__in=print_orders)
        sutra_edition = user_transcription.sutra_edition

        donate_detail_dict = {}
        print_num = 0
        donate_num = 0
        for detail in donate_details:
            if detail.type == enums.PrintDetailType.delivery:
                print_num += detail.print_num
            elif detail.type == enums.PrintDetailType.donation:
                donate_num += detail.donate_num
                temple = detail.temple
                if temple.id not in donate_detail_dict:
                    donate_detail_dict[temple.id] = {
                        "name": temple.name,
                        "num": 0,
                        "url": temple.img_url,
                    }
                donate_detail_dict[temple.id]["num"] += detail.donate_num

        donate_details = [
            donate_detail_dict[temple_id]
            for temple_id in sorted(donate_detail_dict.keys())]
        data = {
            "sutra_edition_name": sutra_edition.name,
            "vow_time": user_transcription.vow_time,
            "finish_time": user_transcription.finish_time,
            "print_num": print_num,
            "donate_num": donate_num,
            "donate_detail": donate_details,
        }
        context = {
            "status": 200,
            "msg": status.HTTP_200_OK,
            "data": data}
        return Response(context)


class PrintHistory(APIView):

    authentication_classes = (SessionAuthentication,)
    permission_classes = (AllowAny,)

    def get(self, request, user_transcription_id):
        details = ModelPrintOrderDetail.objects.filter(
            user=request.user,
            type=enums.PrintDetailType.delivery,
            print_order__status=enums.PrintOrderStatus.paied).order_by(
                "id")
        user_transcription = details.first(
            ).print_order.user_transcription

        if not user_transcription:
            return Response({
                "status": 404,
                "msg": "not found",
            })

        history = []
        for detail in details:
            history.append({
                "time": detail.create_time,
                "num": detail.print_num,
            })

        return Response({
            "status": 200,
            "msg": "success",
            "data": {
                "sutra_edition_name":
                    user_transcription.sutra_edition.name,
                "transcription_finish_time":
                    user_transcription.finish_time,
                "history": history,
            },
        })


class DonateHistory(APIView):

    authentication_classes = (SessionAuthentication,)
    permission_classes = (AllowAny,)

    def get(self, request, user_transcription_id):
        details = ModelPrintOrderDetail.objects.filter(
            user=request.user,
            type=enums.PrintDetailType.donation,
            print_order__status=enums.PrintOrderStatus.paied).order_by(
                "id")
        user_transcription = details.first(
            ).print_order.user_transcription

        if not user_transcription:
            return Response({
                "status": 404,
                "msg": "not found",
            })

        history = []
        for detail in details:
            history.append({
                "time": detail.create_time,
                "num": detail.donate_num,
                "temple_name": detail.temple.name,
            })

        return Response({
            "status": 200,
            "msg": "success",
            "data": {
                "sutra_edition_name":
                    user_transcription.sutra_edition.name,
                "transcription_finish_time":
                    user_transcription.finish_time,
                "history": history,
            },
        })
